//a collection of generic shape objects to be used with LCanvas (requires jQuery).
//The primary job of a shape object is to take inputs such as (x, y) coordinates
//and generate a list of functions and arguments which a canvas 2D context object can apply to draw the shape in question.
//For example, a Polygon object could generate the following to draw a triangle:
//{
//  methods: ["beginPath", "moveTo", "lineTo", "lineTo", "closePath", "fill"],
//  args: [null, [100, 100], [60, 100], [100, 70], null, null]
//}

//define a generic Shape object to provide a base init function, properties
var Shape = function(canv, layer_number, obj_id) {
	this._shape_init(canv, layer_number, obj_id);
};
Shape.prototype._shape_init = function(canv, layer_number, fillStyle, obj_id) {
	if (canv === undefined)
		throw 'cannot create shape without LCanvas object';

	if (canv.ctxs[layer_number] === undefined)
		throw 'cannot create shape without valid layer number';

	this.ctx = canv.ctxs[layer_number];

	if (obj_id !== undefined)
		this.obj_id = obj_id;

	if (fillStyle !== undefined) {
		this.fillStyle = fillStyle;
		this.ctx.fillStyle = fillStyle;
	}

	this.methods = [];
	this.args = [];
};
Shape.prototype.obj_id = null;
Shape.prototype.ctx = null;
Shape.prototype.methods = null;
Shape.prototype.args = null;
Shape.prototype.fillStyle = null;

Shape.prototype.draw = function() {
	this.obj_id = this.ctx.draw(this);
	return this.obj_id;
};
Shape.prototype.generate_methods = function() {
	return {
		methods: [],
		args: []
	};
};
Shape.prototype.animate = function(delta) {
	return this.ctx.animate(this.obj_id, delta);
};
Shape.prototype.animate_to = function(end_state, duration) {
	return this.ctx.animate_to(this.obj_id, end_state, duration);
};

var Circle = function(x, y, radius, canv, layer_number, fillStyle) {
	this._circle_init(x, y, radius, canv, layer_number, fillStyle);
};
$.extend(Circle.prototype, Shape.prototype);

//generic circle shape
Circle.prototype._circle_init = function(x, y, radius, canv, layer_number, fillStyle) {
	if (x === undefined || y === undefined || radius === undefined)
		throw 'missing parameters for circle';
	
	this._shape_init(canv, layer_number, fillStyle);

	var methods = this.generate_methods(x, y, radius, fillStyle);
	this.methods = methods.methods;
	this.args = methods.args;

	this.draw();
};
Circle.prototype.generate_methods = function(x, y, radius, fillStyle) {
	var methods = {
		methods: [],
		args: []
	};
	methods.methods.push('beginPath');
	methods.args.push(null);
	methods.methods.push('arc');
	methods.args.push([x, y, radius, 0, 2 * Math.PI]);
	if (fillStyle !== undefined)
		methods.methods.push('fill');
	else
		methods.methods.push('stroke');
	methods.args.push(null);

	return methods;
};
Circle.prototype.translate_to = function(x, y, duration) {
	if (x === undefined || y === undefined)
		throw 'missing parameters for circle translate_to';
	
	if (duration === undefined)
		duration = 0;
	
	this.animate_to([x, y], duration);
}

//define a generic polygon object which just draws edges connecting the given points and optionally fills the resulting shape
var Polygon = function(points, canv, layer_number, fillStyle) {
	this._polygon_init(points, canv, layer_number, fillStyle);
};
$.extend(Polygon.prototype, Shape.prototype);

Polygon.prototype._polygon_init = function(points, canv, layer_number, fillStyle) {
	if (points === undefined || !points.length)
		throw 'missing parameters for polygon';

	this._shape_init(canv, layer_number, fillStyle);

	var methods = this.generate_methods(points, fillStyle);
	this.methods = methods.methods;
	this.args = methods.args;

	this.draw();
};
Polygon.prototype.generate_methods = function(points, fillStyle) {
	var methods = {
		methods: [],
		args: []
	};
	methods.methods.push('beginPath');
	methods.args.push(null);

	methods.methods.push('moveTo');
	methods.args.push(points.shift());

	for (var i in points) {
		methods.methods.push('lineTo');
		methods.args.push(points[i]);
	}

	methods.methods.push('closePath');
	methods.args.push(null);

	var method;
	if (fillStyle !== undefined)
		method = 'fill';
	else
		method = 'stroke';

	methods.methods.push(method);
	methods.args.push(null);

	return methods;
};
Polygon.prototype.translate_to = function(x, y, duration) {
	if (x === undefined || y === undefined)
		throw 'missing parameters for polygon translate_to';
	
	if (duration === undefined)
		duration = 0;
	
	var end_state = [];
	//assume the x and y are the coords for the first point in the list
	var delta = [x - this.args[1][0], y - this.args[1][1]];
	for (var i in this.args) {
		if (this.args[i].length)
			end_state.push([this.args[i][0] + delta[0], this.args[i][1] + delta[1]]);
		else
			end_state.push(null);
	}

	this.animate_to(end_state, duration);
};

//define rectangle object which uses canvas 2D context's fillRect function
var Rectangle = function(x, y, width, height, canv, layer_number, fillStyle) {
	this._rectangle_init(x, y, width, height, canv, layer_number, fillStyle);
};
$.extend(Rectangle.prototype, Shape.prototype);

Rectangle.prototype._rectangle_init = function (x, y, width, height, canv, layer_number, fillStyle) {
	if (x === undefined || y === undefined || width === undefined || height === undefined)
		throw 'missing parameters for rectangle';

	this._shape_init(canv, layer_number, fillStyle);

	var methods = this.generate_methods(x, y, width, height, fillStyle);
	this.methods = methods.methods;
	this.args = methods.args;

	this.draw();
};
Rectangle.prototype.generate_methods = function(x, y, width, height, fillStyle) {
	return {
		methods: ['fillRect'],
		args: [[x, y, width, height]]
	};
};
Rectangle.prototype.animate_to = function(x, y, width, height, duration) {
	var methods = this.generate_methods(x, y, width, height, this.fillStyle);
	console.log(methods.args);
	Shape.prototype.animate_to.apply(this, [methods.args, duration]);
};
Rectangle.prototype.translate_to = function(x, y, duration) {
	Shape.prototype.animate_to.apply(this, [[[x, y]], duration]);
};

//define right triangle object which extends the generic polygon definition
//the x, y coords given will be the vertex with a right angle
//legs of lengths leg1_length and leg2_length will extend along the x and y axes respectively
var RightTriangle = function(x, y, leg1_length, leg2_length, canv, layer_number, fillStyle) {
	this._right_triangle_init(x, y, leg1_length, leg2_length, canv, layer_number, fillStyle);
};
$.extend(RightTriangle.prototype, Polygon.prototype);

RightTriangle.prototype._right_triangle_init = function(x, y, leg1_length, leg2_length, canv, layer_number, fillStyle) {
	if (x === undefined || y === undefined || leg1_length === undefined || leg2_length === undefined)
		throw 'missing parameters for right triangle';
	
	var points = [[x, y], [x - leg1_length, y], [x, y - leg2_length]];
	this._polygon_init(points, canv, layer_number, fillStyle);

};
RightTriangle.prototype.generate_methods = function(x, y, leg1_length, leg2_length, fillStyle) {
	return Polygon.prototype.generate_methods([[x, y], [x - leg1_length, y], [x, y - leg2_length]], fillStyle);
};
RightTriangle.prototype.animate_to = function(x, y, leg1_length, leg2_length, duration) {
	var methods = this.generate_methods(x, y, leg1_length, leg2_length, this.fillStyle);
	Shape.prototype.animate_to.apply(this, [methods.args, duration]);
};
